
`include "common_header.verilog"

//  *************************************************************************
//  File : pause_txdp_64
//  *************************************************************************
//  This program is controlled by a written license agreement.
//  Unauthorized reproduction or use is expressly prohibited.
//  Copyright (c) 2014 MorethanIP
//  Muenchner Strasse 199, 85757 Karlsfeld, Germany
//  info@morethanip.com
//  http://www.morethanip.com
//  *************************************************************************
//  Designed by : Thomas Schrobenhauser
//  info@morethanip.com
//  *************************************************************************
//  Description : Flow Control (Pause/PFC) Transmit Datapath
//  Version     : $Id: pause_txdp_64.v,v 1.7 2015/10/07 15:17:01 ts Exp $
//  *************************************************************************

module pause_txdp_64 (
   reset_txclk,
   tx_clk,
   tx_clk_ena,
   tx_data_ff,
   tx_sop_ff,
   tx_eop_ff,
   tx_sav_ff,
   tx_empty_ff,
   tx_rden_ff,
   tx_rden_sop_ff,
   tx_rden_sop_pause,
   tx_data_err_ff,
   tx_preamble_ff,
   tx_stat_empty_ff,
   tx_stat_mod_ff,
   tx_stat_err_ff,
   tx_stat_crc_ff,
   tx_stat_id_ff,
   tx_stat_ts_frm_ff,
   tx_data_mac,
   tx_sop_mac,
   tx_eop_mac,
   tx_sav_mac,
   tx_empty_mac,
   tx_rden_mac,
   tx_rden_sop_mac,
   tx_data_err_mac,
   tx_preamble_mac,
   tx_stat_empty_mac,
   tx_stat_mod_mac,
   tx_stat_err_mac,
   tx_stat_crc_mac,
   tx_stat_id_mac,
   tx_stat_ts_frm_mac,
   tx_stat_pgen_mac,
   pause_gen,
   pause_gen_data,
   pause_gen_wcnt,
   pause_wait_t,
   pause_cnt_dec_t,
   tx_enable,
   tx_flush
   );

`include "mtip_40geth_pack_package.verilog"

input   reset_txclk;                            //  Active High reset for tx_clk domain
input   tx_clk;                                 //  XLGMII Transmit clock
input   tx_clk_ena;                             //  XLGMII Transmit clock enable
input   [63:0] tx_data_ff;                      //  Transmit Data from FIFO
input   tx_sop_ff;                              //  Transmit Start of Packet
input   tx_eop_ff;                              //  Transmit End of Packet
input   tx_sav_ff;                              //  Transmit Data Section Available in FIFO
input   tx_empty_ff;                            //  Transmit Data FIFO Empty
output  tx_rden_ff;                             //  Transmit Data FIFO Read enable (FIFO to react with 1 cycle latency)
output  tx_rden_sop_ff;                         //  Transmit reads first word from FIFO
output  tx_rden_sop_pause;                      //  Transmit reads first word from internally generated pause frame
input   tx_data_err_ff;                         //  Memory read error, need to corrupt frame
input   [55:0] tx_preamble_ff;                  //  Transmit Preamble
input   tx_stat_empty_ff;                       //  Transmit Status FIFO Word Empty
input   [2:0] tx_stat_mod_ff;                   //  2:0 Last word modulo; valid with eop
input   tx_stat_err_ff;                         //  3: Error indication from FIFO; valid with eop
input   tx_stat_crc_ff;                         //  4: CRC append indication from FIFO; valid with eop
input   [TSID_WIDTH-1:0] tx_stat_id_ff;         //  Frame identifier
input   tx_stat_ts_frm_ff;                      //  Store timestamp command
output  [63:0] tx_data_mac;                     //  Transmit Data from FIFO
output  tx_sop_mac;                             //  Transmit Start of Packet
output  tx_eop_mac;                             //  Transmit End of Packet
output  tx_sav_mac;                             //  Transmit Data Section Available in FIFO
output  tx_empty_mac;                           //  Transmit Data FIFO Empty
input   tx_rden_mac;                            //  Transmit Data FIFO Read enable (FIFO to react with 1 cycle latency)
input   tx_rden_sop_mac;                        //  Transmit reads first word from FIFO
output  tx_data_err_mac;                        //  Memory read error, need to corrupt frame
output  [55:0] tx_preamble_mac;                 //  Transmit Preamble
output  tx_stat_empty_mac;                      //  Transmit Status FIFO Word Empty
output  [2:0] tx_stat_mod_mac;                  //  2:0 Last word modulo; valid with eop
output  tx_stat_err_mac;                        //  3: Error indication from FIFO; valid with eop
output  tx_stat_crc_mac;                        //  4: CRC append indication from FIFO; valid with eop
output  [TSID_WIDTH-1:0] tx_stat_id_mac;        //  Frame identifier
output  tx_stat_ts_frm_mac;                     //  Store timestamp command
output  tx_stat_pgen_mac;                       //  Internally generated pause frame
input   pause_gen;                              //  Pause frame generate request
input   [63:0] pause_gen_data;                  //  Data from pause generator
output  [2:0] pause_gen_wcnt;                   //  Word counter for pause generator content generation
input   pause_wait_t;                           //  Indicate Tx should pause
output  pause_cnt_dec_t;                        //  MAC Tx statemachine is paused, pause counter may decrement now
input   tx_enable;                              //  Enable MAC transmit path
input   tx_flush;                               //  Flush egress pipeline

wire    tx_rden_ff;
wire    tx_rden_sop_ff;
wire    tx_rden_sop_pause;
wire    [63:0] tx_data_mac;
wire    tx_sop_mac;
wire    tx_eop_mac;
wire    tx_sav_mac;
wire    tx_empty_mac;
wire    tx_data_err_mac;
wire    [55:0] tx_preamble_mac;
wire    tx_stat_empty_mac;
wire    [2:0] tx_stat_mod_mac;
wire    tx_stat_err_mac;
wire    tx_stat_crc_mac;
wire    [TSID_WIDTH-1:0] tx_stat_id_mac;
wire    tx_stat_ts_frm_mac;
wire    tx_stat_pgen_mac;
wire    [2:0] pause_gen_wcnt;
reg     pause_cnt_dec_t;

parameter TX_STM_IDLE      = 2'd 0;
parameter TX_STM_PAUSE_SAV = 2'd 1;
parameter TX_STM_PAUSE_GEN = 2'd 2;
parameter TX_STM_IDLE_WAIT = 2'd 3;

reg     [1:0] state_tx;
reg     [1:0] nextstate_tx;

reg     pause_wait_d1;
reg     pause_gen_req;          //  pause generation request
reg     pause_gen_done;         //  pause generation complete
reg     [2:0] pause_gen_wcnti;  //  word counter when generating data for pause frame transmission
reg     pause_sav;
reg     pause_rden;
reg     pause_sop;
reg     pause_eop;
reg     read_pend;
reg     in_frame_r;
wire    in_frame;
wire    tx_read;


// ---------------------
// Application Interface
// ---------------------

assign tx_rden_ff         = tx_rden_mac       & ~pause_rden;
assign tx_rden_sop_ff     = tx_rden_sop_mac   & ~pause_rden;
assign tx_rden_sop_pause  = tx_rden_sop_mac   &  pause_rden;
assign tx_data_mac        = pause_rden == 1'b 0 ? tx_data_ff : pause_gen_data;
assign tx_sop_mac         = pause_rden == 1'b 0 ? tx_sop_ff  : pause_sop;
assign tx_eop_mac         = pause_rden == 1'b 0 ? tx_eop_ff  : pause_eop;
assign tx_empty_mac       = tx_empty_ff       & ~pause_rden;
assign tx_data_err_mac    = pause_rden == 1'b 0 ? tx_data_err_ff : 1'b 0;
assign tx_preamble_mac    = pause_rden == 1'b 0 ? tx_preamble_ff : 56'h d5555555555555;
assign tx_stat_empty_mac  = tx_stat_empty_ff  | (pause_wait_t & ~tx_flush);
assign tx_stat_mod_mac    = pause_rden == 1'b 0 ? tx_stat_mod_ff : 3'b 100;
assign tx_stat_err_mac    = pause_rden == 1'b 0 ? tx_stat_err_ff : 1'b 0;
assign tx_stat_crc_mac    = pause_rden == 1'b 0 ? tx_stat_crc_ff : 1'b 1;
assign tx_stat_id_mac     = pause_rden == 1'b 0 ? tx_stat_id_ff  : {TSID_WIDTH{1'b 0}};
assign tx_stat_ts_frm_mac = pause_rden == 1'b 0 ? tx_stat_ts_frm_ff : 1'b 0;
assign tx_stat_pgen_mac   = pause_eop;


// ------------------
// Pause MAC Transmit if indicated to do so by Rx datapath
// ------------------

assign tx_sav_mac = (tx_sav_ff & (~pause_wait_t | tx_flush)) |
                     pause_sav;   // pause frame generation

// Indicate Tx is idle to Rx datapath

always @(posedge reset_txclk or posedge tx_clk)
   begin
   if (reset_txclk == 1'b 1)
      begin
      pause_wait_d1 <= 1'b 0;
      pause_cnt_dec_t <= 1'b 0;
      end
   else
      begin
      if (tx_clk_ena == 1'b 1)
         begin
         // delayed pause request
         pause_wait_d1 <= pause_wait_t;

         // Read request has 1 cycle latency after tx_sav_mac assertion,
         // hence need to check for Tx idle with 1 cycle delay.
         if (pause_wait_d1 == 1'b 1 & tx_rden_mac == 1'b 0 & tx_flush == 1'b 0)
            begin
            // Tx is idle (no read activity)
            pause_cnt_dec_t <= 1'b 1;
            end
         else if (pause_wait_d1 == 1'b 0 | tx_flush == 1'b 1)
            begin
            pause_cnt_dec_t <= 1'b 0;
            end
         end
      end
   end


// ---------------------
// Pause Frame Generator
// ---------------------

always @(posedge reset_txclk or posedge tx_clk)
   begin
   if (reset_txclk == 1'b 1)
      begin
      pause_gen_req <= 1'b 0;
      pause_gen_done <= 1'b 0;
      pause_gen_wcnti <= {3{1'b 0}};
      pause_sav <= 1'b 0;
      pause_rden <= 1'b 0;
      pause_sop <= 1'b 0;
      pause_eop <= 1'b 0;
      end
   else
      begin
      if (tx_clk_ena == 1'b 1)
         begin
         //  Request pause frame generation, latch request until frame generation has started
         //  ------------------------------
         if (pause_gen == 1'b 1)
            begin
            pause_gen_req <= 1'b 1;
            end
         else if (state_tx == TX_STM_PAUSE_GEN)
            begin
            pause_gen_req <= 1'b 0;
            end

         //  Indicate tx_sav to MAC
         //  ----------------------
         if (state_tx == TX_STM_IDLE & nextstate_tx == TX_STM_PAUSE_SAV)
            begin
            pause_sav <= 1'b 1;
            end
         else if (state_tx == TX_STM_PAUSE_GEN & nextstate_tx == TX_STM_IDLE_WAIT)
            begin
            pause_sav <= 1'b 0;
            end

         //  During pause read, suppress read from application
         //  -------------------------------------------------
         if (nextstate_tx == TX_STM_PAUSE_SAV | state_tx == TX_STM_PAUSE_SAV |
             state_tx == TX_STM_PAUSE_GEN)
            begin
            pause_rden <= 1'b 1;
            end
         else
            begin
            pause_rden <= 1'b 0;
            end

         if (state_tx == TX_STM_PAUSE_SAV)
            begin
            pause_sop <= 1'b 1;
            end
         else
            begin
            pause_sop <= 1'b 0;
            end

         if (state_tx == TX_STM_PAUSE_GEN & pause_gen_wcnti == 3'b 111)
            begin
            pause_eop <= 1'b 1;
            end
         else
            begin
            pause_eop <= 1'b 0;
            end

         //  Indicate end of pause generation
         //  Keep indication until stm is back to idle to suppress an immediate generation but
         //  instead allow for a user frame transmission after a pause frame.
         //  --------------------------------
         if (state_tx == TX_STM_PAUSE_GEN & pause_gen_wcnti == 3'b 110)
            begin
            pause_gen_done <= 1'b 1;
            end
         else if (state_tx == TX_STM_IDLE)
            begin
            pause_gen_done <= 1'b 0;
            end

         //  Generate frame data
         //  -------------------
         if (nextstate_tx == TX_STM_IDLE)
            begin
            pause_gen_wcnti <= 3'b 000;
            end
         else if (state_tx == TX_STM_PAUSE_SAV & nextstate_tx == TX_STM_PAUSE_GEN)
            begin
            pause_gen_wcnti <= 3'b 001;   //  start
            end
         else if (state_tx == TX_STM_PAUSE_GEN & tx_read == 1'b 1 & pause_gen_wcnti != 3'b 111)
            begin
            pause_gen_wcnti <= pause_gen_wcnti + 3'b 001;
            end
         end
      end
   end

assign pause_gen_wcnt = pause_gen_wcnti;


// STM
// ---

always @(posedge reset_txclk or posedge tx_clk)
   begin
   if (reset_txclk == 1'b 1)
      begin
      state_tx <= TX_STM_IDLE;
      end
   else
      begin
      if (tx_clk_ena == 1'b 1)
         begin
         state_tx <= nextstate_tx;
         end
      end
   end

always @(state_tx or tx_enable or pause_gen_req or pause_gen_done or in_frame or tx_flush or tx_read)
   begin
   case (state_tx)
   TX_STM_IDLE:
      begin
      if (tx_enable == 1'b 1 & pause_gen_req == 1'b 1 & in_frame == 1'b 0 & tx_flush == 1'b 0)
         begin
         //  generate pause frame
         nextstate_tx = TX_STM_PAUSE_SAV;
         end
      else
         begin
         nextstate_tx = TX_STM_IDLE;
         end
      end

   TX_STM_PAUSE_SAV:   // assert tx_sav to MAC
      begin
      if (tx_read == 1'b 1)
         begin
         nextstate_tx = TX_STM_PAUSE_GEN;
         end
      else
         begin
         nextstate_tx = TX_STM_PAUSE_SAV;
         end
      end

   TX_STM_PAUSE_GEN:   // run pause frame generator
      begin
      if (pause_gen_done == 1'b 1)
         begin
         nextstate_tx = TX_STM_IDLE_WAIT;
         end
      else
         begin
         nextstate_tx = TX_STM_PAUSE_GEN;
         end
      end

   TX_STM_IDLE_WAIT:   // allow for a user frame transmission after a pause frame
      begin
      nextstate_tx = TX_STM_IDLE;
      end

   default:
      begin
      nextstate_tx = TX_STM_IDLE;
      end
   endcase
   end


// The MAC issues a final read at the end of tx_eop_ff being asserted. This read must be ignored by the FIFO.
// Here create a flag to indicate possible eop suppression, but only on the cycle following the read.

always @(posedge reset_txclk or posedge tx_clk)
   begin
   if (reset_txclk == 1'b 1)
      begin
      read_pend <= 1'b 0;
      end
   else
      begin
      if (tx_clk_ena == 1'b 1)
         begin
         read_pend <= tx_rden_mac;   // read occurred
         end
      end
   end

always @(posedge reset_txclk or posedge tx_clk)
   begin
   if (reset_txclk == 1'b 1)
      begin
      in_frame_r <= 1'b 0;
      end
   else
      begin
      if (tx_clk_ena == 1'b 1)
         begin
         if (tx_sop_ff & read_pend)
            begin
            in_frame_r <= 1'b 1;
            end
         else if (tx_eop_ff & read_pend)
            begin
            in_frame_r <= 1'b 0;
            end
         end
      end
   end

assign in_frame = (tx_read & ~pause_rden) |   // first read
                  (tx_sop_ff & read_pend) | (in_frame_r & ~(tx_eop_ff & read_pend));

assign tx_read = tx_rden_mac & ~(read_pend & tx_eop_ff & ~tx_rden_sop_mac);   // true read activity, suppress read during eop


endmodule // module pause_txdp_64
